package com.ikun.search.dao;

import com.alibaba.fastjson.JSON;
import com.ikun.common.entity.ElasticsearchEntity;
import com.ikun.common.entity.PageEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author Guio
 */
@Slf4j
@Repository
public class ElasticsearchDao {

    @Resource
    protected RestHighLevelClient client;

    /**
     * 判断索引是否存在
     */
    public boolean existIndex(String indexName) throws IOException {
        //创建索引查询的请求
        GetIndexRequest request = new GetIndexRequest(indexName);
        //判断索引是否存在
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
        log.info("{}是否存在：{}", indexName, exists);
        return exists;
    }

    /**
     * 删除索引
     */
    public void deleteIndex(String indexName) throws IOException {
        //删除索引请求
        DeleteIndexRequest request = new DeleteIndexRequest(indexName);
        //发送删除请求
        AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
        log.info("{}删除成功", indexName);
    }

    /**
     * 创建索引
     */
    public void createIndex(String indexName) throws IOException {
        //创建索引请求
        CreateIndexRequest request = new CreateIndexRequest(indexName);
        // 设置映射
        XContentBuilder mapping = XContentFactory.jsonBuilder()
                .startObject()
                .startObject("properties")
                .startObject("diseaseList")
                .startObject("properties")
                .startObject("dIndex")
                .field("type", "text")
                .field("index", true)
                .field("analyzer", "pinyin")
                .endObject()
                .endObject()
                .endObject()
                .endObject()
                .endObject();
        request.mapping(mapping);
        //发送创建索引请求
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        log.info("{}创建成功", indexName);
    }

    /**
     * 批量插入
     */
    public void insertBatch(String indexName, List<ElasticsearchEntity> list) throws IOException {
        //创建批量操作的请求
        BulkRequest request = new BulkRequest(indexName);
        //请求加入每个插入数据
        list.forEach(entity -> {
            //每个索引请求，设置id和数据
            request.add(new IndexRequest().id(entity.getId()).source(JSON.toJSONString(entity.getData()),
                    XContentType.JSON));
        });
        //执行批量操作
        client.bulk(request, RequestOptions.DEFAULT);
        log.info("批量插入完成");
    }

    /**
     * 通用分页搜索方法
     */
    public <T> PageEntity<T> searchPageByMap(String indexName,
                                             Map<String, String> filters,
                                             Map<String, String> sort,
                                             int from, int size,
                                             Class<T> clazz,
                                             String... highlightFields) throws IOException {
        //创建搜索请求
        SearchRequest request = new SearchRequest(indexName);
        //请求生成器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //创建bool组合查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //如果搜索条件不为空，就设置搜索条件
        if (filters != null && !filters.isEmpty()) {
            for (String key : filters.keySet()) {
                String value = filters.get(key);
                if ("searchInput".equals(key) && !value.isEmpty() && !"undefined".equals(value)) {
                    BoolQueryBuilder searchQuery = QueryBuilders.boolQuery();
                    //医生姓名
                    searchQuery.should(QueryBuilders.matchPhraseQuery("name", value));
                    //科室名称
                    searchQuery.should(QueryBuilders.matchPhraseQuery("department.name", value));
                    //疾病名称
                    searchQuery.should(QueryBuilders.matchPhraseQuery("diseaseList.dName", value));
                    //擅长介绍
                    searchQuery.should(QueryBuilders.matchPhraseQuery("proficientIn", value));
                    //擅长疾病集合
                    searchQuery.should(QueryBuilders.matchPhraseQuery("diseaseList.dName", value));
                    boolQuery.filter(searchQuery);
                } else if (!value.isEmpty() && !"undefined".equals(value)) {
                    //设置过滤条件
                    boolQuery.filter(QueryBuilders.matchPhraseQuery(key, value));
                }
            }
        }
        if (!boolQuery.filter().isEmpty()) {
            //如果设置了过滤条件，就按过滤搜索
            builder.query(boolQuery);
        }
        //设置分页参数
        builder.from(from);
        builder.size(size);
        //设置排序 field type
        if (sort != null && !sort.isEmpty()) {
            builder.sort(sort.get("field"), SortOrder.fromString(sort.get("type")));
        }
        //创建高亮生成器
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        //设置高亮字段
        for (String field : highlightFields) {
            highlightBuilder.field(field);
        }
        //设置前后标签
        highlightBuilder.preTags("<span style='color:red'>").postTags("</span>");
        builder.highlighter(highlightBuilder);
        //执行搜索获得结果
        request.source(builder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHit[] hits = response.getHits().getHits();
        List<T> data = new ArrayList<>();
        //将JSON格式的数据转换为Java对象
        for (SearchHit hit : hits) {
            T res = JSON.parseObject(hit.getSourceAsString(), clazz);
            data.add(res);
            //获得所有高亮的字段
            Map<String, HighlightField> hFields = hit.getHighlightFields();
            for (String hField : hFields.keySet()) {
                //获得反射字段
                try {
                    Field declaredField = clazz.getDeclaredField(hField);
                    //将字段的值替换为带高亮效果的属性值
                    declaredField.setAccessible(true);
                    Text[] fragments = hFields.get(hField).fragments();
                    if (declaredField != null && fragments.length > 0) {
                        declaredField.set(res, fragments[0].string());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        //返回分页数据
        return new PageEntity<>(from, size, response.getHits().getTotalHits().value, data);
    }

    /**
     * 更新docker医生ES
     */
    public void saveOrUpdate(String indexName, ElasticsearchEntity entity) throws IOException {
        //创建索引操作请求
        IndexRequest request = new IndexRequest(indexName);
        //设置一个文档数据
        request.id(entity.getId()).source(JSON.toJSONString(entity.getData()), XContentType.JSON);
        //通过rest客户端发送更新或添加请求
        client.index(request, RequestOptions.DEFAULT);
        log.info("文档保存成功 {}", entity);
    }

    /**
     * 删除docker医生ES
     */
    public void deleteById(String indexName, String id) throws IOException {
        //创建删除文档请求
        DeleteByQueryRequest request = new DeleteByQueryRequest(indexName);
        //设置删除条件
        request.setQuery(QueryBuilders.termQuery("id", id));
        client.deleteByQuery(request, RequestOptions.DEFAULT);
        log.info("文档删除成功 {}", id);
    }

}
